using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using LumiSoft.Net.Log;

namespace LumiSoft.Net.IO
{
    /// <summary>
    /// This class is wrapper to normal stream, provides most needed stream methods which are missing from normal stream.
    /// </summary>
    public class SmartStream : Stream
    {
        private bool     m_IsDisposed    = false;
        private Stream   m_pStream       = null;
        private bool     m_IsOwner       = false;
        private DateTime m_LastActivity;
        private long     m_BytesReaded   = 0;
        private long     m_BytesWritten  = 0;
        private int      m_BufferSize    = 32000;
        private Encoding m_pEncoding     = Encoding.Default;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stream">Stream to wrap.</param>
        /// <param name="owner">Specifies if SmartStream is owner of <b>stream</b>.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public SmartStream(Stream stream,bool owner)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            
            m_pStream = stream;
            m_IsOwner = owner;
        }

        #region method Dispose

        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public new void Dispose()
        {
            if(m_IsDisposed){
                return;
            }
            m_IsDisposed = true;

            if(m_IsOwner){
                m_pStream.Dispose();
            }
        }

        #endregion


        // TODO: Allow to specify if only owner can close/dispose this stream.

        /* Read line asynch implementation:
            Wait for 1 byte, when it reaches, run full read line ?
         
        */

        /// <summary>
        /// 
        /// </summary>
        /// <param name="storeStream"></param>
        /// <param name="count"></param>
        /// <param name="callback"></param>
        /// <param name="tag"></param>
        public void BeginRead(Stream storeStream,int count,ReadToStreamCallback callback,object tag)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="storeStream"></param>
        /// <param name="maxSize"></param>
        /// <param name="exceededAction"></param>
        /// <param name="callback"></param>
        /// <param name="tag"></param>
        public void BeginReadHeader(Stream storeStream,int maxSize,SizeExceededAction exceededAction,ReadToStreamCallback callback,object tag)
        {
        }

        #region method ReadLineString

        /// <summary>
        /// Reads string line from stream with this.Encoing. This method returns null if end of stream reached. 
        /// NOTE: This method isn't performance wise(line buffer is allocated for each line), use it only if you read few lines.
        /// </summary>
        /// <returns>Returns string line or null if end of stream reached.</returns>
        public string ReadLineString()
        {
            int countReaded = 0;
            return ReadLineString(out countReaded);
        }

        /// <summary>
        /// Reads string line from stream with this.Encoing. This method returns null if end of stream reached. 
        /// NOTE: This method isn't performance wise(line buffer is allocated for each line), use it only if you read few lines.
        /// </summary>
        /// <param name="countReaded">Returns raw number of bytes readed from source stream.</param>
        /// <returns>Returns string line or null if end of stream reached.</returns>
        public string ReadLineString(out int countReaded)
        {
            byte[] lineBuffer  = new byte[m_BufferSize];
                   countReaded = 0;
            int rawReaded = ReadLine(lineBuffer,0,lineBuffer.Length,out countReaded,SizeExceededAction.ThrowException);

            if(rawReaded != 0){
                return m_pEncoding.GetString(lineBuffer,0,countReaded);
            }
            else{
                return null;
            }
        }

        /// <summary>
        /// Reads string line from stream with this.Encoing. This method returns null if end of stream reached. 
        /// </summary>   
        /// <param name="buffer">Buffer which to use for line reading.</param>
        /// <param name="offset">Offset in the buffer.</param>
        /// <param name="length">Number of bytes available in buffer.</param>
        /// <param name="countReaded">Returns raw number of bytes readed from source stream.</param>
        /// <returns>Returns string line or null if end of stream reached.</returns>
        public string ReadLineString(byte[] buffer,int offset,int length,out int countReaded)
        {
            countReaded = 0;
            int rawReaded = ReadLine(buffer,offset,length,out countReaded,SizeExceededAction.ThrowException);

            if(rawReaded != 0){
                return m_pEncoding.GetString(buffer,offset,countReaded);
            }
            else{
                return null;
            }
        }

        #endregion

        #region method ReadLine

        /// <summary>
        /// Reads CRLF line from stream.
        /// </summary>
        /// <param name="buffer">Buffer where to store readed line.</param>
        /// <param name="offset">Offset in the buffer.</param>
        /// <param name="count">Number of bytes available in the buffer.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <returns>Returns number of RAW bytes readed from stream. Value 0 means end of stream reached.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="LineSizeExceededException">Is raised when line is bigger than buffer can store.</exception>
        public int ReadLine(byte[] buffer,int offset,int count,SizeExceededAction exceededAction)
        {
            int bytesStored = 0;

            return ReadLine(buffer,offset,count,out bytesStored,exceededAction);
        }

        /// <summary>
        /// Reads CRLF line from stream.
        /// </summary>
        /// <param name="buffer">Buffer where to store readed line.</param>
        /// <param name="offset">Offset in the buffer.</param>
        /// <param name="count">Number of bytes available in the buffer.</param>
        /// <param name="bytesStored">Returns how many bytes actually stored in the buffer.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <returns>Returns number of RAW bytes readed from stream. Value 0 means end of stream reached.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="LineSizeExceededException">Is raised when line is bigger than buffer can store.</exception>
        public int ReadLine(byte[] buffer,int offset,int count,out int bytesStored,SizeExceededAction exceededAction)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            if(offset < 0){
                throw new ArgumentException("Argument 'offset' value must be >= 0.");
            }
            if(count < 0){
                throw new ArgumentException("Argument 'count' value must be >= 0.");
            }
            if(buffer.Length < (count + offset)){
                throw new ArgumentException("Argument 'count' value is bigger than specified 'buffer' can store.");
            }

                 bytesStored    = 0;
            int  rawBytesReaded = 0;
            bool sizeExceeded   = false;
            while(true){
                int currentByte = ReadByte();

                // End of stream reached.
                if(currentByte == -1){
                    break;
                }
                // We have LF, we got a line.
                else if(currentByte == '\n'){
                    rawBytesReaded++;
                    break;
                }
                // We just skip CR, because CR must be with LF, otherwise it's invalid CR.
                else if(currentByte == '\r'){
                    rawBytesReaded++;
                }
                // Normal byte.
                else{
                    rawBytesReaded++;

                    // Line buffer full.
                    if(count == bytesStored){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new LineSizeExceededException();
                        }
                        // If no SizeExceededAction.ThrowException, start junking data.
                        sizeExceeded = true;
                    }
                    else{
                        buffer[offset + bytesStored] = (byte)currentByte;
                        bytesStored++;
                    }
                }
            }

            // Line size exceeded.
            if(sizeExceeded && exceededAction == SizeExceededAction.JunkAndThrowException){
                throw new LineSizeExceededException();
            }

            return rawBytesReaded;
        }

        #endregion

        #region method ReadPeriodTerminated

        /// <summary>
        /// Reads period terminated data from stream and stores to the specified <b>storeStream</b>.
        /// </summary>
        /// <param name="stream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum number of bytes to read. Value -1 means unlimited.</param>
        /// <param name="exceededAction">Specifies how this method behaves when <b>maxSize</b> exceeded.</param>
        /// <returns>Returns how many bytes readed from source stream.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        /// <exception cref="LineSizeExceededException">Is raised when source stream has too big line.</exception>
        /// <exception cref="DataSizeExceededException">Is raised when reading exceeds <b>maxSize</b> specified value.</exception>
        /// <exception cref="IncompleteDataException">Is raised when source stream closed before getting period terminated data.</exception>
        public long ReadPeriodTerminated(Stream stream,long maxSize,SizeExceededAction exceededAction)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("storeStream");
            }

            // NOTE: Reserve last 2 bytes for CRLF, with this we avoid copying buffer to new bigger buffer.
            
            bool   lineSizeExceeded = false;
            long   totalReadedCount = 0;
            byte[] buffer           = new byte[m_BufferSize + 2];
            while(true){
                int bytesStored    = 0;
                int rawReadedCount = 0;
                try{
                    rawReadedCount   = ReadLine(buffer,0,m_BufferSize - 2,out bytesStored,SizeExceededAction.JunkAndThrowException);
                    totalReadedCount += rawReadedCount;
                }
                catch(LineSizeExceededException x){
                    string dummy = x.Message;
                    // If Line size exceeded, we need to read to terminator but junk data.
                    lineSizeExceeded = true;
                }

                // We reached end of stream, incomplete data.
                if(rawReadedCount == 0){
                    throw new IncompleteDataException();
                }
                // We have period terminated terminator(single period on line).
                else if(bytesStored == 1 && buffer[0] == '.'){
                    break;
                }
                // We have normal header line.
                else{
                    // Line size exceeded, we need to read to terminator but junk data.
                    if(lineSizeExceeded){
                    }
                    // Maximum allowed read bytes exceeded.
                    else if(maxSize > -1 && totalReadedCount > maxSize){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new DataSizeExceededException();
                        }
                        // If no SizeExceededAction.ThrowException, start junking data.
                    }
                    else{
                        // Add CRLF.
                        buffer[bytesStored + 0] = (byte)'\r';
                        buffer[bytesStored + 1] = (byte)'\n';

                        // If line starts with period(.), first period is removed.
                        if(bytesStored > 0 && buffer[0] == '.'){
                            stream.Write(buffer,1,bytesStored + 2 - 1);
                        }
                        // Normal line.
                        else{
                            stream.Write(buffer,0,bytesStored + 2);
                        }
                    }
                }
            }

            if(lineSizeExceeded){
                throw new LineSizeExceededException();
            }

            // Maximum allowed read bytes exceeded.
            if(maxSize > -1 && totalReadedCount > maxSize){
                throw new DataSizeExceededException();
            }

            return totalReadedCount;
        }

        #endregion

        #region method ReadHeader

        /// <summary>
        /// Reads header from stream and stores to the specified <b>storeStream</b>.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum number of bytes to read. Value -1 means unlimited.</param>
        /// <param name="exceededAction">Specifies how this method behaves when <b>maxSize</b> exceeded.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        /// <exception cref="LineSizeExceededException">Is raised when source stream has too big line.</exception>
        public void ReadHeader(Stream storeStream,long maxSize,SizeExceededAction exceededAction)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }

            // NOTE: Reserve last 2 bytes for CRLF, with this we avoid copying buffer to new bigger buffer.

            bool   lineSizeExceeded = false;
            long   totalReadedCount = 0;
            byte[] buffer           = new byte[m_BufferSize + 2];
            while(true){
                int bytesStored    = 0;
                int rawReadedCount = 0;
                try{
                    rawReadedCount    = ReadLine(buffer,0,m_BufferSize - 2,out bytesStored,SizeExceededAction.ThrowException);
                    totalReadedCount += rawReadedCount;
                }
                catch(LineSizeExceededException x){
                    string dummy = x.Message;
                    // If Line size exceeded, we need to read to terminator but junk data.
                    lineSizeExceeded = true;
                }

                // We reached end of stream.
                if(rawReadedCount == 0){
                    break;
                }
                // We got header separator "blank line".
                else if(bytesStored == 0){
                    break;
                }
                // We have normal header line.
                else{
                    // Line size exceeded, we need to read to terminator but junk data.
                    if(lineSizeExceeded){
                    }
                    // Maximum allowed read bytes exceeded.
                    else if(maxSize > -1 && totalReadedCount > maxSize){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new DataSizeExceededException();
                        }
                        // If no SizeExceededAction.ThrowException, start junking data.
                    }
                    else{
                        // Add CRLF.
                        buffer[bytesStored + 0] = (byte)'\r';
                        buffer[bytesStored + 1] = (byte)'\n';

                        storeStream.Write(buffer,0,bytesStored + 2);
                    }                    
                }
            }

            if(lineSizeExceeded){
                throw new LineSizeExceededException();
            }

            // Maximum allowed read bytes exceeded.
            if(maxSize > -1 && totalReadedCount > maxSize){
                throw new DataSizeExceededException();
            }
        }

        #endregion

        #region method ReadSpecifiedLengthString

        /// <summary>
        /// Reads specified number of bytes from source stream and converts it to string with current encoding.
        /// </summary>
        /// <param name="length">Number of bytes to read.</param>
        /// <returns>Returns readed data as string.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="IncompleteDataException">Is raised when source stream has not so much data when requested.</exception>
        public string ReadSpecifiedLengthString(int length)
        {
            MemoryStream ms = new MemoryStream();
            ReadSpecifiedLength(ms,length);

            return m_pEncoding.GetString(ms.ToArray());
        }

        #endregion

        #region method ReadSpecifiedLength

        /// <summary>
        /// Reads specified number of bytes from source stream and writes to the specified stream.
        /// </summary>
        /// <param name="stream">Stream where to store readed data.</param>
        /// <param name="length">Number of bytes to read.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        /// <exception cref="IncompleteDataException">Is raised when source stream has not so much data when requested.</exception>
        public void ReadSpecifiedLength(Stream stream,int length)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            int    totalReadedCount = 0;
            byte[] buffer           = new byte[32000];
            while(totalReadedCount < length){
                int readedCount = this.Read(buffer,0,Math.Min(buffer.Length,length - totalReadedCount));
                // End of stream reached, no tenough data.
                if(readedCount == 0){
                    throw new IncompleteDataException();
                }
                totalReadedCount += readedCount;

                stream.Write(buffer,0,readedCount);
            }
        }

        #endregion

        #region method ReadAll

        /// <summary>
        /// Reads all data from source stream and stores to the specified stream.
        /// </summary>
        /// <param name="stream">Stream where to store readed data.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void ReadAll(Stream stream)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            byte[] buffer = new byte[32000];
            while(true){
                int readedCount = Read(buffer,0,buffer.Length);
                // End of stream reached, we readed file sucessfully.
                if(readedCount == 0){
                    break;
                }
                else{
                    stream.Write(buffer,0,readedCount);
                }
            }
        }

        #endregion

        #region method Write

        /// <summary>
        /// Writes specified string data to stream.
        /// </summary>
        /// <param name="data">Data to write.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>data</b> is null.</exception>
        public void Write(string data)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(data == null){
                throw new ArgumentNullException("data");
            }

            byte[] dataBytes = Encoding.Default.GetBytes(data);
            Write(dataBytes,0,dataBytes.Length);
            Flush();
        }

        #endregion

        #region method WriteLine

        /// <summary>
        /// Writes specified line to stream. If CRLF is missing, it will be added automatically to line data.
        /// </summary>
        /// <param name="line">Line to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>line</b> is null.</exception>
        /// <returns>Returns number of raw bytes written.</returns>
        public int WriteLine(string line)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(line == null){
                throw new ArgumentNullException("line");
            }

            if(!line.EndsWith("\r\n")){
                line += "\r\n";
            }

            byte[] dataBytes = m_pEncoding.GetBytes(line);
            Write(dataBytes,0,dataBytes.Length);
            Flush();

            return dataBytes.Length;
        }

        #endregion

        #region method WriteStream

        /// <summary>
        /// Writes all source <b>stream</b> data to stream.
        /// </summary>
        /// <param name="stream">Stream which data to write.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void WriteStream(Stream stream)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            byte[] buffer = new byte[m_BufferSize];
            while(true){
                int readed = stream.Read(buffer,0,buffer.Length);
                // We readed all data.
                if(readed == 0){
                    break;
                }
                Write(buffer,0,readed);
            }
            Flush();
        }

        /// <summary>
        /// Writes specified number of bytes from source <b>stream</b> to stream.
        /// </summary>
        /// <param name="stream">Stream which data to write.</param>
        /// <param name="count">Number of bytes to write.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when <b>count</b> argument has invalid value.</exception>
        public void WriteStream(Stream stream,long count)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(count < 0){
                throw new ArgumentException("Argument 'count' value must be >= 0.");
            }

            byte[] buffer      = new byte[m_BufferSize];
            long   readedCount = 0;
            while(readedCount < count){
                int readed = stream.Read(buffer,0,(int)Math.Min(buffer.Length,count - readedCount));
                readedCount += readed;
                Write(buffer,0,readed);
            }
            Flush();
        }

        #endregion

        #region method WritePeriodTerminated

        /// <summary>
        /// Reads all data from the source <b>stream</b> and writes it to stream. Period handling and period terminator is added as required.
        /// </summary>
        /// <param name="stream">Source stream which data to write to stream.</param>
        /// <returns>Returns number of bytes written to source stream.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        /// <exception cref="LineSizeExceededException">Is raised when <b>stream</b> has too big line.</exception>        
        public long WritePeriodTerminated(Stream stream)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            // NOTE: Reserve first byte in buffer for additional period and last 2 bytes for CRLF, with
            // this we avoid copying buffer to new bigger buffer.

            // We need to read lines, do period handling and write them to stream.
            long        totalWritten = 0;
            byte[]      buffer       = new byte[m_BufferSize + 3];
            SmartStream reader       = new SmartStream(stream,false);
            while(true){
                int bytesStored = 0;
                int readedCount = reader.ReadLine(buffer,1,buffer.Length - 3,out bytesStored,SizeExceededAction.ThrowException);
                // We have readed all data.
                if(readedCount == 0){
                    break;
                }
                
                // Add CRLF.
                buffer[bytesStored + 1] = (byte)'\r';
                buffer[bytesStored + 2] = (byte)'\n';

                // Period handling. If line starts with period(.), additional period is added.
                if(bytesStored > 0 && buffer[1] == '.'){          
                    // Add additional period.
                    buffer[0] = (byte)'.';

                    Write(buffer,0,bytesStored + 1 + 2);
                    totalWritten += bytesStored + 1 + 2;
                }
                // Just write readed line.
                else{
                    // We skip first byte what was reserverd for additional period.
                    Write(buffer,1,bytesStored + 2);
                    totalWritten += bytesStored + 2;
                }
            }

            // Write period terminator.
            WriteLine(".");

            Flush();

            return totalWritten;
        }

        #endregion

        #region method WriteHeader

        /// <summary>
        /// Reads header from source <b>stream</b> and writes it to stream.
        /// </summary>
        /// <param name="stream">Stream from where to read header.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void WriteHeader(Stream stream)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            SmartStream reader = new SmartStream(stream,false);
            reader.ReadHeader(this,-1,SizeExceededAction.ThrowException);
        }

        #endregion


        #region override method Flush

        /// <summary>
        /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        public override void Flush()
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException("SmartStream");
            }

            m_pStream.Flush();
        }

        #endregion

        #region override method Seek

        /// <summary>
        /// Sets the position within the current stream.
        /// </summary>
        /// <param name="offset">A byte offset relative to the <b>origin</b> parameter.</param>
        /// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
        /// <returns>The new position within the current stream.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        public override long Seek(long offset,SeekOrigin origin)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException("SmartStream");
            }

            return m_pStream.Seek(offset,origin);
        }

        #endregion

        #region override method SetLength

        /// <summary>
        /// Sets the length of the current stream.
        /// </summary>
        /// <param name="value">The desired length of the current stream in bytes.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        public override void SetLength(long value)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException("SmartStream");
            }

            m_pStream.SetLength(value);
        }

        #endregion

        #region override method Read

        /// <summary>
        /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
        /// </summary>
        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
        /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
        /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        public override int Read(byte[] buffer,int offset,int count)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException("SmartStream");
            }

            int readedCount = m_pStream.Read(buffer,offset,count);

            m_LastActivity = DateTime.Now;
            m_BytesReaded += readedCount;

            return readedCount;
        }

        #endregion

        #region override method Write

        /// <summary>
        /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        public override void Write(byte[] buffer,int offset,int count)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException("SmartStream");
            }            

            m_pStream.Write(buffer,offset,count);
            
            m_LastActivity = DateTime.Now;
            m_BytesWritten += count;
        }

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets this stream underlying stream.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public Stream SourceStream
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream; 
            }
        }

        /// <summary>
        /// Gets if SmartStream is owner of source stream. This property affects like closing this stream will close SourceStream if IsOwner true.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public bool IsOwner
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_IsOwner; 
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                m_IsOwner = value;
            }
        }

        /// <summary>
        /// Gets the last time when data was read or written.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public DateTime LastActivity
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_LastActivity; 
            }
        }

        /// <summary>
        /// Gets how many bytes are readed through this stream.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public long BytesReaded
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_BytesReaded; 
            }
        }

        /// <summary>
        /// Gets how many bytes are written through this stream.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public long BytesWritten
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_BytesWritten;
            }
        }

        /// <summary>
        /// Gets or sets string related methods default encoding.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when null value is passed.</exception>
        public Encoding Encoding
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pEncoding; 
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }
                if(value == null){
                    throw new ArgumentNullException();
                }

                m_pEncoding = value;
            }
        }


        /// <summary>
        /// Gets a value indicating whether the current stream supports reading.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public override bool CanRead
        { 
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream.CanRead;
            } 
        }

        /// <summary>
        /// Gets a value indicating whether the current stream supports seeking.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public override bool CanSeek
        { 
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream.CanSeek;
            } 
        }

        /// <summary>
        /// Gets a value indicating whether the current stream supports writing.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public override bool CanWrite
        { 
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream.CanWrite;
            } 
        }

        /// <summary>
        /// Gets the length in bytes of the stream.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public override long Length
        { 
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream.Length;
            } 
        }

        /// <summary>
        /// Gets or sets the position within the current stream.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public override long Position
        { 
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                return m_pStream.Position;
            } 

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException("SmartStream");
                }

                m_pStream.Position = value;
            }
        }

        #endregion

    }
}
